﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using SlimDX;
using SlimDX.Direct3D9;

namespace Editor
{
    internal class RenderUtils
    {
        private static SlimDX.Direct3D9.PresentParameters m_PresentParameters;
        public static SlimDX.Direct3D9.PresentParameters PresentParameters
        {
            get { return m_PresentParameters; }
        }

        private static SlimDX.Direct3D9.Device m_Device;
        public static SlimDX.Direct3D9.Device Direct3DDevice
        {
            get { return m_Device; }
        }

        private static SlimDX.Direct3D9.Font m_Direct3DFont;
        public static SlimDX.Direct3D9.Font Direct3DFont
        {
            get { return m_Direct3DFont; }
        }

        public static bool CreateDirect3DDevice(IntPtr windowHandle)
        {
            if (m_Device != null)
            {
                return true;
            }

            m_PresentParameters = new SlimDX.Direct3D9.PresentParameters()
            {
                BackBufferWidth = 32,
                BackBufferHeight = 32,
                EnableAutoDepthStencil = true,
                AutoDepthStencilFormat = SlimDX.Direct3D9.Format.D24S8,
                BackBufferFormat = SlimDX.Direct3D9.Format.A8R8G8B8,
                Windowed = true,
                DeviceWindowHandle = windowHandle
            };

            m_Device = new SlimDX.Direct3D9.Device(new SlimDX.Direct3D9.Direct3D(),
                                                   0,
                                                   SlimDX.Direct3D9.DeviceType.Hardware,
                                                   windowHandle,
                                                   SlimDX.Direct3D9.CreateFlags.HardwareVertexProcessing,
                                                   m_PresentParameters);

            if (m_Device != null)
            {
                using (System.Drawing.Font font = new System.Drawing.Font(FontFamily.GenericSansSerif, 9))
                {
                    m_Direct3DFont = new SlimDX.Direct3D9.Font(m_Device, font);
                }
            }

            return (m_Device != null);
        }

        public static SlimDX.Direct3D9.SwapChain CreateSwapChain(int width, int height, IntPtr windowHandle)
        {
            SlimDX.Direct3D9.PresentParameters presentParams = m_PresentParameters;
            presentParams.BackBufferWidth = width;
            presentParams.BackBufferHeight = height;
            presentParams.EnableAutoDepthStencil = false;
            presentParams.DeviceWindowHandle = windowHandle;

            return new SlimDX.Direct3D9.SwapChain(m_Device, presentParams);
        }

        public static void DestroySwapChain(SlimDX.Direct3D9.SwapChain swapChain)
        {
            swapChain.Dispose();
        }

        public static void ResetDirect3DDevice(int width, int height, IntPtr windowHandle)
        {
            m_PresentParameters.BackBufferWidth = width;
            m_PresentParameters.BackBufferHeight = height;
            m_PresentParameters.DeviceWindowHandle = windowHandle;

            m_Device.Reset(m_PresentParameters);
        }

        public static void GetVertexElementsForShader(SlimDX.Direct3D9.Device device, SlimDX.Direct3D9.Effect effect, out List<SlimDX.Direct3D9.VertexElement> vertexElements)
        {
            SlimDX.Direct3D9.EffectHandle origTechnique = effect.Technique;

            Dictionary<ShaderSemantic, bool> usedSemantics = new Dictionary<ShaderSemantic, bool>();
            device.BeginScene();

            SlimDX.Direct3D9.EffectHandle technique = null;
            while ((technique = effect.FindNextValidTechnique(technique)) != null)
            {
                effect.Technique = technique;

                int numPasses = effect.Begin();
                for (int pass = 0; pass < numPasses; pass++)
                {
                    effect.BeginPass(pass);
                    {
                        SlimDX.Direct3D9.VertexShader vs = device.VertexShader;
                        SlimDX.Direct3D9.ShaderBytecode byteCode = vs.Function;
                        SlimDX.Direct3D9.ShaderSemantic[] inputSemantics = byteCode.GetInputSemantics();
                        foreach (ShaderSemantic semantic in inputSemantics)
                        {
                            usedSemantics[semantic] = true;
                        }
                    }
                    effect.EndPass();
                }
                effect.End();
            }

            device.EndScene();

            DeclarationType[] elementTypes = new DeclarationType[]
            {
                DeclarationType.Float3,    //Position = 0,
                DeclarationType.Float4,    //BlendWeight = 1,
                DeclarationType.Short4,    //BlendIndices = 2,
                DeclarationType.Float3,    //Normal = 3,
                DeclarationType.Float1,    //PointSize = 4,
                DeclarationType.Float2,    //TextureCoordinate = 5,
                DeclarationType.Float3,    //Tangent = 6,
                DeclarationType.Float3,    //Binormal = 7,
                DeclarationType.Unused,    //TessellateFactor = 8,
                DeclarationType.Float2,    //PositionTransformed = 9,
                DeclarationType.Color,     //Color = 10,
                DeclarationType.Unused,    //Fog = 11,
                DeclarationType.Unused,    //Depth = 12,
                DeclarationType.Unused     //Sample = 13,
            };
            short[] elementSizes = new short[]
            {
                12, //Position = 0,
                16, //BlendWeight = 1,
                8,  //BlendIndices = 2,
                12, //Normal = 3,
                4,  //PointSize = 4,
                8,  //TextureCoordinate = 5,
                12, //Tangent = 6,
                12, //Binormal = 7,
                -1, //TessellateFactor = 8,
                8,  //PositionTransformed = 9,
                4,  //Color = 10,
                -1, //Fog = 11,
                -1, //Depth = 12,
                -1  //Sample = 13,
            };

            vertexElements = new List<VertexElement>();
            short offset = 0;
            foreach (ShaderSemantic semantic in usedSemantics.Keys)
            {
                DeclarationType dt = elementTypes[(int)semantic.Usage];
                vertexElements.Add(new VertexElement(0, offset, dt, DeclarationMethod.Default, semantic.Usage, (byte)semantic.UsageIndex));
                offset += elementSizes[(int)semantic.Usage];
            }
            vertexElements.Add(VertexElement.VertexDeclarationEnd);
        }
    }
}
